package fr.umlv.m2.jee.spij.service.remedial.impl;

import fr.umlv.m2.jee.spij.persistence.actor.StudentRemedial;
import fr.umlv.m2.jee.spij.persistence.actor.TeacherRemedial;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateStudentRemedialDAO;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateTeacherRemedialDAO;
import fr.umlv.m2.jee.spij.persistence.education.Remedial;
import fr.umlv.m2.jee.spij.persistence.education.Unit;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateRemedialDAO;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateUnitDAO;
import fr.umlv.m2.jee.spij.service.ldap.authentification.DisplayActor;
import fr.umlv.m2.jee.spij.service.remedial.DisplayRemedial;
import fr.umlv.m2.jee.spij.service.remedial.IDisplayRemedialService;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Default service about remedials
 *
 * @author mathieu mazzolini
 */
@Service("defaultDisplayRemedialService")
public class DefaultDisplayRemedialService implements IDisplayRemedialService {

    @Autowired
    private HibernateRemedialDAO remedialDAO;

    @Autowired
    private HibernateStudentRemedialDAO studentRemedialDAO;

    @Autowired
    private HibernateTeacherRemedialDAO teacherRemedialDAO;

    @Autowired
    private HibernateUnitDAO unitDAO;

    DefaultDisplayRemedialService() {
    }

    /**
     * @param unitId
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public List<DisplayRemedial> getAllDisplayRemedial(long unitId, MessageContext context) {
        final Unit unit = unitDAO.findById(unitId);
        final List<Remedial> remedials = unit.getRemedials();
        final List<DisplayRemedial> displayRemedials = new ArrayList<DisplayRemedial>();

        if (remedials == null) {
            context.addMessage(new MessageBuilder().error().code("remedials.empty").build());
        } else {
            for (Remedial remedial : remedials) {
                DisplayRemedial displayRemedial = new DisplayRemedial(remedial);
                if (unit != null)
                    displayRemedial.setUnitName(unit.getName());
                displayRemedials.add(displayRemedial);
            }
        }
        return displayRemedials;
    }

    /**
     * @param unitId      the unit foreign key
     * @param currentUser
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public List<DisplayRemedial> getAllDisplayRemedialPerUser(final long unitId, DisplayActor currentUser, MessageContext context) {
        if (currentUser.isTeacher() || currentUser.isStudent()) {
            final Unit unit = unitDAO.findById(unitId);
            final List<Remedial> Remedials = remedialDAO.findByLdapActorAndUnitId(unitId, currentUser.getLogin(), currentUser.getType());
            final List<DisplayRemedial> displayRemedials = new ArrayList<DisplayRemedial>();

            if (Remedials.isEmpty()) {
                context.addMessage(new MessageBuilder().error().code("remedials.empty").build());
            } else {
                for (Remedial remedial : Remedials) {
                    DisplayRemedial displayRemedial = new DisplayRemedial(remedial);
                    if (unit != null)
                        displayRemedial.setUnitName(unit.getName());
                    displayRemedials.add(displayRemedial);
                }
            }
            return displayRemedials;
        }

        return getAllDisplayRemedial(unitId, context);
    }

    /**
     * @param id
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public DisplayRemedial getRemedialById(long id, MessageContext context) {
        final Remedial remedial = remedialDAO.findById(id);
        final DisplayRemedial displayRemedial = new DisplayRemedial(remedial);

        if (remedial == null) {
            context.addMessage(new MessageBuilder().error().code("remedial.empty").build());
        } else {
            if (remedial.getUnit() != null) {
                displayRemedial.setUnitName(unitDAO.findById(remedial.getUnit()).getName());
            }
        }
        return displayRemedial;
    }

    /**
     * @param displayRemedial
     * @param unitId
     * @param context
     *
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public boolean createRemedial(DisplayRemedial displayRemedial, long unitId, MessageContext context) {
        Remedial remedial = new Remedial();
        remedial.setUnit(unitId);
        remedial.setSubject(displayRemedial.getSubject());

        if (displayRemedial.getWeight() <= 0) {
            context.addMessage(new MessageBuilder().error().code("remedial.weight.positive").build());
            return false;
        }
        remedial.setWeight(displayRemedial.getWeight());

        Date date = displayRemedial.getDate();
        DateTime startTime = new DateTime(date.getTime());
        startTime = startTime.plus(displayRemedial.getStartTime().getTime());
        DateTime endTime = new DateTime(date.getTime());
        endTime = endTime.plus(displayRemedial.getEndTime().getTime());

        if (startTime.isAfter(endTime)) {
            context.addMessage(new MessageBuilder().error().code("remedial.startTime.isAfter.endTime").build());
            return false;
        }

        final Interval duration = new Interval(startTime, endTime);
        remedial.setDuration(duration);

        final Date consultationDate = displayRemedial.getConsultationDate();
        final Date consultationHour = displayRemedial.getConsultationHour();
        if (consultationDate != null && consultationHour != null) {
            DateTime consultation = new DateTime(consultationDate.getTime());
            consultation = consultation.plus(consultationHour.getTime());
            if (endTime.isAfter(consultation)) {
                context.addMessage(new MessageBuilder().error().code("remedial.consultation.isBefore.endTime").build());
                return false;
            }
            remedial.setConsultation(consultation);
        } else if (consultationDate != null) {
            context.addMessage(new MessageBuilder().error().code("remedial.consultation.incomplete").build());
            return false;
        } else if (consultationHour != null) {
            context.addMessage(new MessageBuilder().error().code("remedial.consultation.incomplete").build());
            return false;
        }

        remedialDAO.persist(remedial);
        return true;
    }

    /**
     * @param modifiedRemedial
     * @param context
     *
     * @return
     */
    @Transactional(readOnly = false)
    @Override
    public boolean updateRemedialById(DisplayRemedial modifiedRemedial, MessageContext context) {
        Remedial remedial = remedialDAO.findById(modifiedRemedial.getId());
        boolean hasBeenModified = false;

        if (remedial != null) {
            if (!remedial.getSubject().equals(modifiedRemedial.getSubject())) {
                hasBeenModified = true;
                remedial.setSubject(modifiedRemedial.getSubject());
            }
            if (remedial.getWeight() != modifiedRemedial.getWeight()) {
                if (modifiedRemedial.getWeight() <= 0) {
                    context.addMessage(new MessageBuilder().error().code("remedial.weight.positive").build());
                    return false;
                }
                hasBeenModified = true;
                remedial.setWeight(modifiedRemedial.getWeight());
            }

            //Duration update
            Date date = modifiedRemedial.getDate();
            DateTime startTime = new DateTime(date.getTime());
            startTime = startTime.plus(modifiedRemedial.getStartTime().getTime());
            DateTime endTime = new DateTime(date.getTime());
            endTime = endTime.plus(modifiedRemedial.getEndTime().getTime());

            DateTime previousStartDate = remedial.getDuration().getStart();
            DateTime previousEndDate = remedial.getDuration().getEnd();

            if (startTime.isAfter(endTime)) {
                context.addMessage(new MessageBuilder().error().code("remedial.startTime.isAfter.endTime").build());
                return false;
            }

            //Consultation update
            final Date consultationDate = modifiedRemedial.getConsultationDate();
            final Date consultationHour = modifiedRemedial.getConsultationHour();

            DateTime previousConsultation = remedial.getConsultation();

            if (consultationDate != null && consultationHour != null) {
                DateTime consultation = new DateTime(consultationDate.getTime());
                consultation = consultation.plus(consultationHour.getTime());
                if (endTime.isAfter(consultation)) {
                    context.addMessage(new MessageBuilder().error().code("remedial.consultation.isBefore.endTime").build());
                    return false;
                }
                if (!consultation.equals(previousConsultation)) {
                    hasBeenModified = true;
                    remedial.setConsultation(consultation);
                }
            } else if (consultationDate != null) {
                context.addMessage(new MessageBuilder().error().code("remedial.consultation.incomplete").build());
                return false;
            } else if (consultationHour != null) {
                context.addMessage(new MessageBuilder().error().code("remedial.consultation.incomplete").build());
                return false;
            } else {
                if (previousConsultation != null) {
                    hasBeenModified = true;
                    remedial.setConsultation(null);
                }
            }

            if (!previousStartDate.equals(startTime) || !previousEndDate.equals(endTime)) {
                hasBeenModified = true;
                remedial.setDuration(new Interval(startTime, endTime));
            }

            remedialDAO.merge(remedial);
        }

        if (!hasBeenModified) {
            context.addMessage(new MessageBuilder().error().code("updateRemedialFail").build());
        }

        return hasBeenModified;
    }

    /**
     * @param id
     * @param context
     *
     * @return
     */
    @Transactional(readOnly = false)
    @Override
    public boolean deleteRemedialById(long id, MessageContext context) {
        Remedial remedial = remedialDAO.findById(id);
        List<StudentRemedial> students = remedial.getStudents();
        for (StudentRemedial studentRemedial : students) {
            studentRemedialDAO.remove(studentRemedial);
            studentRemedialDAO.flush();
        }
        List<TeacherRemedial> teachers = remedial.getTeachers();
        for (TeacherRemedial teacherRemedial : teachers) {
            teacherRemedialDAO.remove(teacherRemedial);
            teacherRemedialDAO.flush();
        }

        remedialDAO.remove(remedial);
        remedialDAO.flush();
        return true;
    }
}
